#include "event_monitor.h"
#include <X11/Xlibint.h>
#include <X11/keysym.h>
#include <QGuiApplication>

// 因为 XRecord 的事件循环会堵塞当前线程，避免监听事件的时候应用程序卡主
// 我们建立一个继承于 QThread 的KeyEventMonitor类，通过子线程进行事件监听操作
KeyEventMonitor::KeyEventMonitor(QObject *parent) : QThread(parent)
{
    //m_useAsync = true;
}

void KeyEventMonitor::startMonitor()
{
    m_exiting = false;
    start();
}

void KeyEventMonitor::stopMonitor(bool waitExit)
{
    m_exiting = true;
    if (!m_useAsync && m_display_ctrl)
    {
        Display* tmp = m_display_ctrl;
        m_display_ctrl = nullptr;
        XRecordDisableContext (tmp, m_context);
        XRecordFreeContext (tmp, m_context);
        XCloseDisplay (m_display_data);
        XCloseDisplay (tmp);
        m_display_data = nullptr;
        m_context = 0;
    }
    if (waitExit) wait();
}

bool KeyEventMonitor::setMonitShortcutKeys(const QString &name, const QString &keySequences)
{
    if (name.isEmpty() || keySequences.isEmpty())
        return false;
    QKeySequence keySeq = QKeySequence::fromString(keySequences);
    if (keySeq.isEmpty())
        return false;
    m_cutKeys[name] = keySeq;
    return true;
}

void KeyEventMonitor::setPause(bool pause)
{
    m_isPaused = pause;
}

QString KeyEventMonitor::getShortcutKeys(const QString &name)
{
    auto it = m_cutKeys.find(name);
    if ( it != m_cutKeys.end())
    {
        return it.value().toString();
    }
    return QString();
}

void KeyEventMonitor::run()
{
    // 创建记录 XRecord 协议的 X 专用连接
    m_display_ctrl = XOpenDisplay(nullptr);
    // 连接打开检查
    if (m_display_ctrl == nullptr) {
        fprintf(stderr, "unable to open display\n");
        return;
    }    // 建立一个专门读取 XRecord 协议数据的 X 链接
    m_display_data = XOpenDisplay(nullptr);
    // 连接打开检查
    if (m_display_data == nullptr) {
        fprintf(stderr, "unable to open second display\n");
        return;
    }
    XSynchronize(m_display_ctrl,True);

    // 初始化 XRecordCreateContext 所需的 XRecordClientSpec 参数
    // XRecordAllClients 的意思是 "记录所有 X Client" 的事件
    // Receive from ALL clients, including future clients.
    XRecordClientSpec clients = XRecordAllClients;
    // 创建 XRecordRange 变量，XRecordRange 用于控制记录事件的范围
    XRecordRange* range = XRecordAllocRange();
    // 记录事件范围检查
    if (range == nullptr){
        fprintf(stderr, "unable to allocate XRecordRange\n");
        return;
    }
    // 初始化记录事件范围，范围开头设置成 KeyPress, 范围结尾设置成 MotionNotify 后
    // 事件的类型就包括 KeyPress、KeyRelase、ButtonPress、ButtonRelease、MotionNotify五种事件
    // Receive KeyPress, KeyRelease, ButtonPress, ButtonRelease and MotionNotify events.
    memset(range, 0, sizeof(XRecordRange));
    range->device_events.first = KeyPress;
    range->device_events.last  = MotionNotify;
    // 根据上面的记录客户端类型和记录事件范围来创建　“记录上下文”
    // 然后把 XRecordContext 传递给 XRecordEnableContext 函数来开启事件记录循环
    // And create the XRECORD context.
    m_context = XRecordCreateContext(m_display_ctrl, 0, &clients, 1, &range, 1);
    if (m_context == 0) {
        fprintf(stderr, "XRecordCreateContext failed\n");
        return;
    }
    // 释放 range 指针
    XFree(range);
    // XSync 的作用就是把上面的 X 代码立即发给 X Server
    // 这样 X Server 接受到事件以后会立即发送给 XRecord 的 Client 连接
    XSync(m_display_ctrl, True);

    if (!m_useAsync)
    {
        // 调用 XRecordEnableContext 函数建立 XRecord 上下文
        // XRecordEnableContext 函数一旦调用就开始进入堵塞时的事件循环，直到线程或所属进程结束
        // X Server 事件一旦发生就传递给事件处理回调函数
        if (!XRecordEnableContext(m_display_data, m_context,  callback, (XPointer) this)) {
            fprintf(stderr, "XRecordEnableContext() failed\n");
            return;
        }
    }
    else
    {
        if (!XRecordEnableContextAsync(m_display_data, m_context, callback, (XPointer) this)) {
            fprintf (stderr, "Cound not enable the record context!/n");
            return;
        }

        while (!m_exiting) {
            XRecordProcessReplies (m_display_data);
            msleep(100);
        }
    }

    if (m_display_ctrl)
    {
        XRecordDisableContext (m_display_ctrl, m_context);
        XRecordFreeContext (m_display_ctrl, m_context);
        XCloseDisplay (m_display_data);
        XCloseDisplay (m_display_ctrl);
        m_display_data = nullptr;
        m_display_ctrl = nullptr;
        m_context = 0;
    }

}

void KeyEventMonitor::checkShortcutKey(KeyCode code)
{
    if (m_isPaused) return;
    XID c = XKeycodeToKeysym(m_display_ctrl, code, 0);
    char keyName[16] = {0};
    if (c == XK_space)
    {
        qstrcpy(keyName, "Space");
    }
    else if (c > XK_space && c <= XK_asciitilde)
    {
        keyName[0] = char(c - XK_space + ' ');
    }
    else if (c >= XK_F1 && c <= XK_F12)
    {
        sprintf(keyName, "F%d", int(c - XK_F1 + 1));
    }
    else if (c == XK_Print)
    {
        qstrcpy(keyName, "Print");
    }
    else if (c == XK_BackSpace)
    {
        qstrcpy(keyName, "Backspace");
    }
    else if (c == XK_Tab)
    {
        qstrcpy(keyName, "Tab");
    }
    else if (c == XK_Return)
    {
        qstrcpy(keyName, "Return");
    }
    else if (c == XK_Pause)
    {
        qstrcpy(keyName, "Pause");
    }
    else if (c == XK_Scroll_Lock)
    {
        qstrcpy(keyName, "ScrollLock");
    }
    else if (c == XK_Escape)
    {
        qstrcpy(keyName, "Esc");
    }
    else if (c == XK_Delete || XK_KP_Delete)
    {
        qstrcpy(keyName, "Del");
    }
    else if (c == XK_Insert)
    {
        qstrcpy(keyName, "Ins");
    }
    else if (c == XK_Menu)
    {
        qstrcpy(keyName, "Menu");
    }
    else if (c == XK_Num_Lock)
    {
        qstrcpy(keyName, "NumLock");
    }
    else if (c == XK_Caps_Lock)
    {
        qstrcpy(keyName, "CapsLock");
    }
    else if (c == XK_Home || c == XK_KP_Home)
    {
        qstrcpy(keyName, "Home");
    }
    else if (c == XK_End || c == XK_KP_End)
    {
        qstrcpy(keyName, "End");
    }
    else if (c == XK_Page_Up || c == XK_KP_Page_Up)
    {
        qstrcpy(keyName, "PgUp");
    }
    else if (c == XK_Page_Down || c == XK_KP_Page_Down)
    {
        qstrcpy(keyName, "PgDown");
    }
    else if (c == XK_Left || c == XK_KP_Left)
    {
        qstrcpy(keyName, "Left");
    }
    else if (c == XK_Up || c == XK_KP_Up)
    {
        qstrcpy(keyName, "Up");
    }
    else if (c == XK_Right || c == XK_KP_Right)
    {
        qstrcpy(keyName, "Right");
    }
    else if (c == XK_Down || c == XK_KP_Down)
    {
        qstrcpy(keyName, "Down");
    }
    else if (c == XK_KP_Divide)
    {
        keyName[0] = '/';
    }
    else if (c == XK_KP_Multiply)
    {
        keyName[0] = '*';
    }
    else if (c == XK_KP_Add)
    {
        keyName[0] = '+';
    }
    else if (c == XK_KP_Subtract)
    {
        keyName[0] = '-';
    }
    else if (c == XK_KP_Enter)
    {
        qstrcpy(keyName, "Enter");
    }

    //char * key = XKeysymToString(c);
    //fprintf (stderr, "KeyPress: %s, Modifiers:%08X\n", keyName, QGuiApplication::queryKeyboardModifiers());
    //XK_Caps_Lock
    if (keyName[0])
    {
        QString keys;
        Qt::KeyboardModifiers modifiers = QGuiApplication::queryKeyboardModifiers();
        if (modifiers & Qt::MetaModifier)
        {
            keys.append("Meta+");
        }
        if (modifiers & Qt::ControlModifier)
        {
            keys.append("Ctrl+");
        }
        if (modifiers & Qt::AltModifier)
        {
            keys.append("Alt+");
        }
        if (modifiers & Qt::ShiftModifier)
        {
            keys.append("Shift+");
        }
        keys.append(keyName);
        QKeySequence keySeq = QKeySequence::fromString(keys);
        for (auto k = m_cutKeys.begin(); k != m_cutKeys.end(); ++k)
        {
            if (k.value().matches(keySeq) == QKeySequence::ExactMatch)
            {
                emit shortcutKeyPress(k.key());
                break;
            }
        }
    }
}

void KeyEventMonitor::callback(XPointer ptr, XRecordInterceptData* data)
{
    ((KeyEventMonitor *) ptr)->handleRecordEvent(data);
}
// 真实处理 X 事件监听的回调函数
void KeyEventMonitor::handleRecordEvent(XRecordInterceptData* data)
{
    if (data->category != XRecordFromServer)
    {
        XRecordFreeData (data);
        return;
    }

    // 得到 xEvent 对象
    xEvent * event = (xEvent *)data->data;
    int event_type = event->u.u.type;
    BYTE keycode = event->u.u.detail;
    int rootx = event->u.keyButtonPointer.rootX;
    int rooty = event->u.keyButtonPointer.rootY;
    Time time = data->server_time;
    static int cur_x = 0;
    static int cur_y = 0;
    switch (event_type)
    {
    case ButtonPress:
        emit mousePress(keycode, rootx, rooty);
        break;
    case MotionNotify:
        emit mouseMove(keycode, rootx, rooty);
        cur_x = rootx;
        cur_y = rooty;
        break;
    case ButtonRelease:
        emit mouseRelease(keycode, rootx, rooty);
        break;
    case KeyPress:
        emit keyPress(keycode);
        checkShortcutKey(keycode);
        break;
    case KeyRelease:
        emit keyRelease(keycode);
        break;
    default:
        break;
    }
    // 资源释放
    fflush(stdout);
    XRecordFreeData(data);
}
// 过滤滚轮事件
bool KeyEventMonitor::filterWheelEvent(int detail)
{
    return detail != WheelUp && detail != WheelDown && detail != WheelLeft && detail != WheelRight;
}
